using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using OpenFileDialogEx.Controls;
using OpenFileDialogEx.OS;

namespace SP
{
    /// <summary>
    /// A property bag for the Shader class
    /// Usage:
    /// propertyGrid1.SelectedObject = new ShaderPropertyBag2(mShader);
    /// </summary>
    public class ShaderPropertyBag3 : PropertyBag
    {
        private const string cCatPrefix = "- ";
        private const string cPPPrefix = "#";

        public event System.EventHandler ValueChanged;

        public ShaderPropertyBag3(Shader shader, PropertyGrid propGrid)
        {
            mShader = shader;
            mPropGrid = propGrid;

            this.GetValue += new PropertySpecEventHandler(PropertyBag_GetValue);
            this.SetValue += new PropertySpecEventHandler(PropertyBag_SetValue);
            RefreshPropertySpec();
        }

        private void RefreshPropertySpec()
        {
            PS_AddShader();

            // build feature hash table and add properties of fusions
            mFusionHashTbl = new System.Collections.Hashtable();

            foreach (IFusion f in mShader.Fusions)
            {
                string fName = f.Name;

                // hash to mFusionHashTbl by name
                mFusionHashTbl.Add(fName, f);

                // add 'Enabled' properties
                Properties.Add(new PropertySpec(fName + ".Enabled", typeof(bool), cCatPrefix + "ShadingFeatures", f.Desc));

                if (!f.Enabled) continue;

                // preprocessors
                foreach (Preprocessor pp in f.Preprocessors)
                {
                    Properties.Add(PS_AddPreprocessor(fName, pp));
                }

                // parameters
                foreach (IParameter pa in f.Parameters)
                {
                    if (!pa.Editable)
                        continue;

                    Properties.Add(PS_AddParameter(fName, pa));
                }
            }
        }

        private void PS_AddShader()
        {
            PropertySpec ps;

            // add shader properties
            ps = new PropertySpec("Name", typeof(string), cCatPrefix + "Common");
            ps.Attributes = new Attribute[] { ReadOnlyAttribute.Yes };
            Properties.Add(ps);

            ps = new PropertySpec("Reactor", typeof(string), cCatPrefix + "Common");
            ps.Attributes = new Attribute[] { ReadOnlyAttribute.Yes };
            Properties.Add(ps);

            ps = new PropertySpec("Transparent", typeof(bool), cCatPrefix + "RenderStates");
            Properties.Add(ps);

            ps = new PropertySpec("2-Sided", typeof(bool), cCatPrefix + "RenderStates");
            Properties.Add(ps);

            ps = new PropertySpec("DepthTest", typeof(bool), cCatPrefix + "RenderStates");
            Properties.Add(ps);
        }

        private static PropertySpec PS_AddParameter(string fName, IParameter pa)
        {
            PropertySpec ps;

            if (pa is Parameter.TextureParamter)
            {
                ps = new PropertySpec(
                    pa.Name
                    , typeof(string)
                    , fName
                    , pa.Desc
                    , pa.Value
                    , typeof(TextureEditor2)
                    , ""
                    );
            }
            else
            {
                ps = new PropertySpec(
                    pa.Name
                    , typeof(string)
                    , fName
                    , pa.Desc
                    );
            }
            return ps;
        }

        private static PropertySpec PS_AddPreprocessor(string fName, Preprocessor pp)
        {
            PropertySpec ps;
            ps = new PropertySpec(
                String.Format("{0}{1}", cPPPrefix, pp.Name)
                , typeof(int)
                , fName
                , pp.Desc
                );
            return ps;
        }

        void PropertyBag_SetValue(object sender, PropertySpecEventArgs e)
        {
            PropertySpec ps = e.Property;

            if (ps.Category.StartsWith(cCatPrefix))
            {
                // #Common, #Renderstates, and #ShadingFeatures
                switch (e.Property.Name)
                {
                    case "Name":
                        mShader.Name = (string)e.Value;
                        break;

                    case "Transparent":
                        mShader.Transparent = (bool)e.Value;
                        break;

                    case "2-Sided":
                        mShader.TwoSided = (bool)e.Value;
                        break;

                    case "DepthTest":
                        mShader.DepthTest = (bool)e.Value;
                        break;

                    default:
                        {
                            string[] tokens = ps.Name.Split(new char[] { '.' });
                            string fName = tokens[0];
                            IFusion f = mFusionHashTbl[fName] as IFusion;
                            if (null != f)
                            {
                                f.Enabled = (bool)e.Value;

                                if (null != mPropGrid)
                                {
                                    this.Properties.Clear();
                                    RefreshPropertySpec();
                                    mPropGrid.Refresh();
                                }
                            }
                        }
                        break;
                }
            }
            else
            {
                // Preprocessors and Parameters
                string fName = ps.Category;
                IFusion f = mFusionHashTbl[fName] as IFusion;

                if (null != f)
                {
                    if (ps.Name.StartsWith(cPPPrefix))
                    {
                        string pName = ps.Name.Substring(1);
                        Preprocessor pp = f.GetPreprocessor(pName);
                        if (null != pp) pp.Value = (int)e.Value;
                    }
                    else
                    {
                        string pName = ps.Name;
                        IParameter pa = f.GetParameter(pName);
                        if (null != pa) pa.Value = (string)e.Value;
                    }
                }
            }

            if(null != this.ValueChanged)
                this.ValueChanged.Invoke(this, new EventArgs());
        }

        private void PropertyBag_GetValue(object sender, PropertySpecEventArgs e)
        {
            PropertySpec ps = e.Property;

            if (ps.Category.StartsWith(cCatPrefix))
            {
                // #Common, #Renderstates, and #ShadingFeatures
                switch (e.Property.Name)
                {
                    case "Name":
                        e.Value = mShader.Name;
                        break;

                    case "Reactor":
                        e.Value = mShader.Reactor;
                        break;

                    case "Transparent":
                        e.Value = mShader.Transparent;
                        break;

                    case "2-Sided":
                        e.Value = mShader.TwoSided;
                        break;

                    case "DepthTest":
                        e.Value = mShader.DepthTest;
                        break;

                    default:
                        {
                            string[] tokens = ps.Name.Split(new char[] { '.' });
                            string fName = tokens[0];
                            IFusion f = mFusionHashTbl[fName] as IFusion;
                            if (null != f) e.Value = f.Enabled;
                        }
                        break;

                }
            }
            else
            {
                // Preprocessors and Parameters
                string fName = ps.Category;
                IFusion f = mFusionHashTbl[fName] as IFusion;

                if (null != f)
                {
                    if (ps.Name.StartsWith(cPPPrefix))
                    {
                        string pName = ps.Name.Substring(1);
                        Preprocessor pp = f.GetPreprocessor(pName);
                        if (null != pp) e.Value = pp.Value;
                    }
                    else
                    {
                        string pName = ps.Name;
                        IParameter pa = f.GetParameter(pName);
                        if (null != pa) e.Value = pa.Value;
                    }
                }
            }
        }

        private Shader mShader;
        private PropertyGrid mPropGrid;
        private System.Collections.Hashtable mFusionHashTbl;
    }


    /// <summary>
    /// A property bag for the Shader class
    /// Usage:
    /// propertyGrid1.SelectedObject = new ShaderPropertyBag2(mShader);
    /// </summary>
    public class ShaderPropertyBag2 : PropertyBag
    {
        public ShaderPropertyBag2(Shader shader)
        {
            mShader = shader;

            this.GetValue += new PropertySpecEventHandler(PropertyBag_GetValue);
            this.SetValue += new PropertySpecEventHandler(PropertyBag_SetValue);

            PropertySpec ps;

            // add shader properties
            ps = new PropertySpec("Name", typeof(string), "Common");
            ps.Attributes = new Attribute[] { ReadOnlyAttribute.Yes };
            Properties.Add(ps);

            ps = new PropertySpec("Reactor", typeof(string), "Common");
            ps.Attributes = new Attribute[] { ReadOnlyAttribute.Yes };
            Properties.Add(ps);

            ps = new PropertySpec("Transparent", typeof(bool), "Effect");
            Properties.Add(ps);

            ps = new PropertySpec("2-Sided", typeof(bool), "Effect");
            Properties.Add(ps);

            ps = new PropertySpec("DepthTest", typeof(bool), "Effect");
            Properties.Add(ps);

            // build feature hash table and add properties of fusions
            mFusionHashTbl = new System.Collections.Hashtable();

            foreach (IFusion f in mShader.Fusions)
            {
                string fName = f.Name;
                ps = new PropertySpec(fName + ".Enabled", typeof(bool), "Fusions", f.Desc);
                Properties.Add(ps);

                // preprocessors
                foreach (Preprocessor pp in f.Preprocessors)
                {
                    ps = new PropertySpec(String.Format("{0}.{1}", fName, pp.Name), typeof(int), "Preprocessors", pp.Desc);
                    Properties.Add(ps);
                }

                // parameters
                foreach (IParameter pa in f.Parameters)
                {
                    if (!pa.Editable)
                        continue;

                    if (pa is Parameter.TextureParamter)
                    {
                        ps = new PropertySpec(
                            String.Format("{0}.{1}", fName, pa.Name)
                            , typeof(string)
                            , "Parameters"
                            , pa.Desc
                            , pa.Value
                            , typeof(TextureEditor2)
                            , ""
                            );
                    }
                    else
                    {
                        ps = new PropertySpec(String.Format("{0}.{1}", fName, pa.Name), typeof(string), "Parameters", pa.Desc);
                    }
                    Properties.Add(ps);
                }

                // hash to mFusionHashTbl by name
                mFusionHashTbl.Add(fName, f);
            }
        }

        void PropertyBag_SetValue(object sender, PropertySpecEventArgs e)
        {
            string[] tokens = ExtractTokens(e.Property);

            if (tokens.Length > 1)
            {
                string fName = tokens[0];
                string pName = tokens[1];
                IFusion f = mFusionHashTbl[fName] as IFusion;

                switch (e.Property.Category)
                {
                    case "Fusions":
                        {
                            if (pName == "Enabled")
                                f.Enabled = (bool)e.Value;

                        } break;

                    case "Preprocessors":
                        {
                            Preprocessor pp = f.GetPreprocessor(pName);
                            if (null != pp) pp.Value = (int)e.Value;

                        } break;

                    case "Parameters":
                        {
                            IParameter pa = f.GetParameter(pName);
                            if (null != pa) pa.Value = (string)e.Value;

                        } break;
                }
            }
            else
            {
                // shader common properties
                switch (e.Property.Name)
                {
                    case "Name":
                        mShader.Name = (string)e.Value;
                        break;

                    case "Transparent":
                        mShader.Transparent = (bool)e.Value;
                        break;

                    case "2-Sided":
                        mShader.TwoSided = (bool)e.Value;
                        break;

                    case "DepthTest":
                        mShader.DepthTest = (bool)e.Value;
                        break;
                }
            }
        }

        private void PropertyBag_GetValue(object sender, PropertySpecEventArgs e)
        {
            string[] tokens = ExtractTokens(e.Property);

            if (tokens.Length > 1)
            {
                string fName = tokens[0];
                string pName = tokens[1];
                IFusion f = mFusionHashTbl[fName] as IFusion;

                switch (e.Property.Category)
                {
                    case "Fusions":
                        {
                            if (pName == "Enabled")
                                e.Value = f.Enabled;

                        } break;

                    case "Preprocessors":
                        {
                            Preprocessor pp = f.GetPreprocessor(pName);
                            if (null != pp) e.Value = pp.Value;

                        } break;

                    case "Parameters":
                        {
                            IParameter pa = f.GetParameter(pName);
                            if (null != pa) e.Value = pa.Value;

                        } break;
                }
            }
            else
            {
                // shader common properties
                switch (e.Property.Name)
                {
                    case "Name":
                        e.Value = mShader.Name;
                        break;

                    case "Reactor":
                        e.Value = mShader.Reactor;
                        break;

                    case "Transparent":
                        e.Value = mShader.Transparent;
                        break;

                    case "2-Sided":
                        e.Value = mShader.TwoSided;
                        break;

                    case "DepthTest":
                        e.Value = mShader.DepthTest;
                        break;
                }
            }
        }

        private string[] ExtractTokens(PropertySpec spec)
        {
            return spec.Name.Split(new char[] { '.' });
        }

        private Shader mShader;
        private System.Collections.Hashtable mFusionHashTbl;
    }

}
